use crate::ai_bid::{AiBidNormal, AiBidSolo, IAiBidMode};
use crate::ai_play::{AiPlayNormal, AiPlaySolo, IAiPlayMode};
use crate::card_weight::CardWeight;
use crate::table::Table;
use crate::turn::{TurnCard, TurnHandPileState};
use crate::utils::Utils;
use crate::round::Round;
use log::info;
use poker_lib::{PokerCard, PokerCards, Suit};

#[allow(dead_code)]
#[derive(Ord, PartialOrd, Eq, Debug, Copy, Clone, PartialEq)]
enum AiDifficulty {
    Easy,
    Normal,
    Hard,
}

const DEFAULT_DIFFICULTY: AiDifficulty = AiDifficulty::Hard;

pub fn ai_bid(seat_id: usize, table: &mut Table, round: &mut Round) -> i16 {
    let seat = &round.seats[seat_id];
    //模式和最终分数
    let mode = table.mode;
    let final_score = table.final_score;
    //不同模式的叫牌策略
    let mut abn = AiBidNormal;
    let abn2 = AiBidNormal;
    let mut abs = AiBidSolo { normal: abn2 };
    let bid: &mut dyn IAiBidMode = match mode {
        1 => &mut abn,
        2 => &mut abs,
        _ => &mut abs,
    };

    let bn = match DEFAULT_DIFFICULTY {
        AiDifficulty::Easy => bid.get_ak_num(seat),
        AiDifficulty::Normal => (bid.get_bid_normal(seat) + 0.5).floor() as i16,
        AiDifficulty::Hard => bid.get_bid_with_hard_diff(seat, &table, &round, final_score) as i16,
    };

    info!("robot {} bid : {:?} cards : {:?}", seat.id, bn, seat.cards);
    return bn;
}

pub fn ai_play(seat_id: usize, table: &mut Table, round: &mut Round) -> PokerCard {
    //计算可以出的牌
    let seat = &round.seats[seat_id];
    // let card_can_play = &get_playable_cards(round, seat_id);
    let card_can_play = &seat.cards_can.cards;
    if card_can_play.len() == 1 {
        return card_can_play[0];
    }
    //计算每张可以出的牌的权重
    let mut card_weights = Vec::new();
    for item in card_can_play {
        card_weights.push(CardWeight::new(*item, 0, 0, 0));
    }
    let seat = &round.seats[seat_id];
    //计算历史 card suit map
    let history_suit_map: &PokerCards = &history_suit_piles(round.cards_out());
    let hand_pile_state = compute_hand_pile_state(seat_id, round, history_suit_map);
    let mode = table.mode;

    //不同模式的打牌策略
    let mut pn = AiPlayNormal;
    let mut ps = AiPlaySolo;
    let play: &mut dyn IAiPlayMode = match mode {
        1 => &mut pn,
        2 => &mut ps,
        _ => &mut ps,
    };

    let mut turn_trick_state = play.get_turn_trick_state(seat_id, table, round, &card_can_play);
    turn_trick_state.history_suit_piles = history_suit_piles(round.cards_out());
    //测试最高难度
    let rule_type = play.compute_rule_type(
        &seat,
        round,
        &turn_trick_state,
        &hand_pile_state,
        true,
        table.limit_bag,
    );
    play.compute_rule_point(
        &mut card_weights,
        table,
        round,
        &seat,
        &hand_pile_state,
        &turn_trick_state,
        rule_type,
    );
    let out_card;
    if !card_weights.is_empty() {
        card_weights.sort();
        out_card = card_weights[0].card;
    } else {
        out_card = card_can_play[card_can_play.len() - 1];
    }
    info!(
        "seat {} play card with rule {} card {}",
        seat_id,
        rule_type.to_string(),
        out_card
    );
    out_card
}

fn compute_hand_pile_state(
    seat_id: usize,
    round: &Round,
    history_suit_map: &PokerCards,
) -> TurnHandPileState {
    let mut hand_pile_state = TurnHandPileState::new();
    let seat = &round.seats[seat_id];
    let card_can_play = &seat.cards_can.cards;
    let total_cards = &seat.cards.cards;
    let hand_suit_map = &mut hand_pile_state.pile_suit_group;
    for card in total_cards {
        hand_suit_map.add(*card);
        if Utils::check_pile_has_card(card_can_play, card) {
            let rank = card.get_number();
            //中位牌
            if rank >= 8 && rank <= 11 {
                hand_pile_state.mid_cards.push(*card);
            }
            if card.is_trump_card() {
                hand_pile_state.spades_pile.push(*card);
            }
            if card.suit == Suit::Joker {
                hand_pile_state.spades_count += 1
            }
        }
    }
    hand_pile_state.playable_pile = card_can_play.clone();

    for i in 0..=3 {
        let suit = Suit::from_u8(i).unwrap();
        //某个花色的所有牌
        let hand_pile = hand_suit_map[suit];
        let hand_suit_cards = hand_suit_map.get_suit_pocker_cards(suit);
        let round_history_pile = history_suit_map[suit];
        //计算空缺牌
        if hand_pile.n == 0 {
            hand_pile_state.void_suits.push(i);
            continue;
        }
        //计算高位牌
        for num in (0..hand_suit_cards.len()).rev() {
            let card = hand_suit_cards[num];
            // let (_, card_suit) = card.get_suit_num();
            let card_suit = card.suit;
            // 获取与card相同花色的本轮牌桌的牌
            let mut out_piles: Vec<PokerCard> = Vec::new();
            let turn_cards = &round.cur_turn().turn_cards;
            for out_card in turn_cards {
                if out_card.card.suit == card_suit {
                    out_piles.push(out_card.card);
                }
            }
            let num2 = (14 - card.get_number()) as i16;
            let num3 = (hand_suit_cards.len() - num - 1) as i16;
            let mut is_biggest = true;
            if num3 < num2 {
                is_biggest = true;
                for i in (card.get_number() + 1)..=14 {
                    let rank = i;
                    if !(check_bucket_has_rank(&round_history_pile.bucket, rank)
                        || check_bucket_has_rank(&hand_pile.bucket, rank))
                        || Utils::check_pile_has_rank(&out_piles, rank)
                    {
                        is_biggest = false;
                        break;
                    }
                }
            }
            if is_biggest {
                if Utils::check_pile_has_card(&card_can_play, &card) {
                    hand_pile_state.big_cards.push(card);
                    //如果是大的黑桃
                    if card_suit == Suit::Spade {
                        hand_pile_state.spades_count += 1;
                    }
                }
            }
        }
        //计算低位牌
        for num in 0..hand_suit_cards.len() {
            let card2 = &hand_suit_cards[num];
            let card_suit = card2.suit;
            let card_rank = card2.get_number();
            // 获取与card相同花色的本轮牌桌的牌
            let mut out_piles: Vec<PokerCard> = Vec::new();
            let turn_cards = &round.cur_turn().turn_cards;
            for out_card in turn_cards {
                if out_card.card.suit == card_suit {
                    out_piles.push(out_card.card);
                }
            }
            let num4 = card_rank - 1;
            let num5 = num as i16;
            let mut is_lower = true;
            if num5 < num4 {
                for k in 2..card_rank {
                    let rank2 = k;
                    if rank2 > 14 {
                        is_lower = false;
                        break;
                    }
                    if !(check_bucket_has_rank(&round_history_pile.bucket, rank2)
                        || check_bucket_has_rank(&hand_pile.bucket, rank2))
                        || Utils::check_pile_has_rank(&out_piles, rank2)
                    {
                        is_lower = false;
                        break;
                    }
                }
            }
            if is_lower && Utils::check_pile_has_card(&card_can_play, &card2) {
                hand_pile_state.lowest_cards.push(*card2);
            }
        }
    }
    hand_pile_state
}
///对历史出牌进行花色分组 也可以在出牌阶段结算 记录到round里
fn history_suit_piles(card_out: Vec<&TurnCard>) -> PokerCards {
    let mut cards = PokerCards::new();
    // let card_out = &round.cards_out;
    for card in card_out {
        cards.add(card.card);
    }
    cards
}
///按照花色对牌进行分组
// fn group_pile_by_suit(card: &PokerCard, groups: &mut HashMap<u8, Vec<PokerCard>>) {
//     let suit = card.get_suit(); // Implement the logic to get the suit of a card
//     groups.entry(suit).or_insert(Vec::new()).push(*card);
// }
//注意这里比较的是牌值 2-14
pub fn check_bucket_has_rank(pile: &[u8; 14], rank: i16) -> bool {
    let mut index = rank as usize;
    if index == 14 {
        index = 1
    }
    pile[index] > 0
}
